package core.reports;

import static org.junit.Assert.*;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import common.util.DateUtils;

import core.Amount;
import core.Barcode;
import core.CoreFacade;
import core.DataManager;
import core.Item;
import core.Product;
import core.ProductGroup;
import core.ProductManager;
import core.ReportConstants;
import core.StorageUnit;
import core.Unit;

public class ProdStatsGuideTest 
{
	private static CoreFacade model = null;
	
	@BeforeClass
	public static void setUpBeforeClass() throws Exception 
	{
		System.out.println("Starting Product Statistics Guide Tests");
		System.out.println("SETUP: Start");
		//setup code
		CoreFacade.setDBMode(false);
		DataManager.setMode(false);
		DataManager.setTestMode(true);
		
		System.out.println("SETUP: DONE");
	}

	@AfterClass
	public static void tearDownAfterClass() throws Exception 
	{
		System.out.println("CLEANUP: Start");
		
		//clean up code
		CoreFacade.destroyInstance();
		
		System.out.println("CLEANUP: DONE");
		System.out.println("Finished Product Statistics Guide Test");
	}
	
	@Before
	public void setUp() throws Exception 
	{
		System.out.println("-TEST: START");
		System.out.println("-TEST SETUP: START");
		
		System.out.println("-- Destroy Instance");
		CoreFacade.destroyInstance();
		model = null;
		
		System.out.println("-- Crate new Instance");
		model = CoreFacade.getInstance();
		
		System.out.println("-TEST SETUP: DONE");
	}

	@After
	public void tearDown() throws Exception 
	{
		System.out.println("-TEST CLEANUP: START");
		//clean up everything we added
		System.out.println("-- Destroy Instance");
		CoreFacade.destroyInstance();
		model = null;
		
		System.out.println("-TEST CLEANUP: DONE");
		System.out.println("-TEST: DONE");
	}
/*	
	Test 5: Test a product that all the items have been removed.
	a.	All items removed before reporting period
	b.	All items removed during reporting period
	c.	Product added and all items removed in the reporting period
*/
	@Test
	public void allItemsRemovedBeforePeriod()
	{
		try {
			String entry = "07/20/2011 02:00 AM";
			Calendar entryDate = createCalendar(entry);
			// Create a storage Unit
			StorageUnit unit = model.addStorageUnit("testUnit1");
			// add product
			Product p = createProduct("testProdBarcode", "TestProduct", Unit.count,
									  0f, 0, 0, entry);
			model.addProduct(p);
			// Add 3 items of a product
			Item item1 = model.addItem("testProdBarcode", entryDate, 0, unit, 0, true);
			Item item2 = model.addItem("testProdBarcode", entryDate, 0, unit, 0, false);
			Item item3 = model.addItem("testProdBarcode", entryDate, 0, unit, 0, false);
			
			// Remove the items
			model.removeItem(item1);
			model.removeItem(item2);
			model.removeItem(item3);
			
			// update the remove date so its not today
			Calendar exitTime = createCalendar("07/30/2012 05:00 PM");
			item1.setExitTime(exitTime);
			item2.setExitTime(exitTime);
			item3.setExitTime(exitTime);
			
			// Run the report			
			ProductStatisticsGuide guide = new ProductStatisticsGuide(3);
			List<ProductStatisticsNode> nodes = guide.getProductStatistics();
			assertTrue(nodes.size() == 1);
			
			ProductStatisticsNode node = nodes.get(0);
			assertTrue(node.getUsedAvg().contains("377.0 days"));
			assertTrue(node.getUsedMax().contains("377 days"));
			
			
		} catch (ParseException e) {
			System.out.println(e.getMessage());
			//e.printStackTrace();
			fail();
		} catch (Exception e){
			System.out.println(e.getMessage());
			e.printStackTrace();
			fail();
		}
		
	}
	
/*	
	Test 5: Test a product that all the items have been removed.
	b.	All items removed during reporting period
	c.	Product added and all items removed in the reporting period
*/
	@Test
	public void allItemsRemovedDuringPeriod()
	{
		try {
			String entry = "07/20/2012 02:00 AM";
			Calendar entryDate = createCalendar(entry);
			// Create a storage Unit
			StorageUnit unit = model.addStorageUnit("testUnit2");
			// add product
			Product p = createProduct("testProdBarcode", "TestProduct", Unit.count,
									  0f, 0, 0, entry);
			model.addProduct(p);
			// Add 3 items of a product
			Item item1 = model.addItem("testProdBarcode", entryDate, 0, unit, 0, true);
			Item item2 = model.addItem("testProdBarcode", entryDate, 0, unit, 0, false);
			Item item3 = model.addItem("testProdBarcode", entryDate, 0, unit, 0, false);
			
			// Remove the items
			model.removeItem(item1);
			model.removeItem(item2);
			model.removeItem(item3);
			
			// update the remove date so its not today
			Calendar exitTime = createCalendar("11/26/2012 05:00 PM");
			item1.setExitTime(exitTime);
			item2.setExitTime(exitTime);
			item3.setExitTime(exitTime);
			
			// Run the report
			ProductStatisticsGuide guide = new ProductStatisticsGuide(3);
			List<ProductStatisticsNode> nodes = guide.getProductStatistics();
			assertTrue(nodes.size() == 1);
			
			ProductStatisticsNode node = nodes.get(0);
			String test = node.getSupplyCur();
			assertTrue(test.contains("0"));	
			
		} catch (ParseException e) {
			System.out.println(e.getMessage());
			//e.printStackTrace();
			fail();
		} catch (Exception e){
			System.out.println(e.getMessage());
			//e.printStackTrace();
			fail();
		}
		
	}
	
	/*	
	Test 5: Test a product that all the items have been removed.
	c.	Product added and all items removed in the reporting period
*/
	@Test
	public void allItemsRemovedDuringPeriod2()
	{
		try {
			String entry = "11/20/2012 02:00 AM";
			Calendar entryDate = createCalendar(entry);
			// Create a storage Unit
			StorageUnit unit = model.addStorageUnit("testUnit3");
			// add product
			Product p = createProduct("newBarcode", "TestProduct", Unit.count,
									  0f, 0, 0, entry);
			model.addProduct(p);
			// Add 3 items of a product
			Item item1 = model.addItem("newBarcode", entryDate, 0, unit, 0, true);
			Item item2 = model.addItem("newBarcode", entryDate, 0, unit, 0, false);
			Item item3 = model.addItem("newBarcode", entryDate, 0, unit, 0, false);
			
			// Remove the items
			model.removeItem(item1);
			model.removeItem(item2);
			model.removeItem(item3);
			
			// update the remove date so its not today
			Calendar exitTime = createCalendar("11/26/2012 05:00 PM");
			item1.setExitTime(exitTime);
			item2.setExitTime(exitTime);
			item3.setExitTime(exitTime);
			
			// Run the report
			ProductStatisticsGuide guide = new ProductStatisticsGuide(3);
			List<ProductStatisticsNode> nodes = guide.getProductStatistics();
			assertTrue(nodes.size() == 1);
			
			ProductStatisticsNode node = nodes.get(0);
			String test = node.getSupplyCur();
			assertTrue(test.contains("0"));	
			
			assertTrue(true);
			
		} catch (ParseException e) {
			System.out.println(e.getMessage());
			//e.printStackTrace();
			fail();
		} catch (Exception e){
			System.out.println(e.getMessage());
			//e.printStackTrace();
			fail();
		}
		
	}
/*	
	Test 6: Test a product in which no items have been used
	a.	No items used before OR during reporting period
	b.	No items used during reporting period
	c.	Product added and no items removed in the reporting period
*/
	@Test
	public void noItemsBeforeorDuringTests()
	{
		try {
			String entry = "04/20/2012 02:00 AM";
			Calendar entryDate = createCalendar(entry);
			// Create a storage Unit
			StorageUnit unit = model.addStorageUnit("testUnit4");
			// add product
			Product p = createProduct("barcode4", "TestProduct", Unit.count,
									  0f, 0, 0, entry);
			model.addProduct(p);
			// Add 3 items of a product
			Item item1 = model.addItem("barcode4", entryDate, 0, unit, 0, true);
			Item item2 = model.addItem("barcode4", entryDate, 0, unit, 0, false);
			Item item3 = model.addItem("barcode4", entryDate, 0, unit, 0, false);
			
			// Run the report
			ProductStatisticsGuide guide = new ProductStatisticsGuide(3);
			List<ProductStatisticsNode> nodes = guide.getProductStatistics();
			assertTrue(nodes.size() == 1);
			
			ProductStatisticsNode node = nodes.get(0);
			String test = node.getSupplyCur();
			assertTrue(test.contains("3"));	
			
		} catch (ParseException e) {
			System.out.println(e.getMessage());
			//e.printStackTrace();
			fail();
		} catch (Exception e){
			System.out.println(e.getMessage());
			//e.printStackTrace();
			fail();
		}
		
	}
	
/*	
	Test 6: Test a product in which no items have been used
	b.	No items used during reporting period
	c.	Product added and no items removed in the reporting period
*/
	@Test
	public void noItemsDuringTests()
	{
		try {
			String entry = "04/20/2012 02:00 AM";
			Calendar entryDate = createCalendar(entry);
			// Create a storage Unit
			StorageUnit unit = model.addStorageUnit("testUnit5");
			// add product
			Product p = createProduct("barcode5", "TestProduct", Unit.count,
									  0f, 0, 0, entry);
			model.addProduct(p);
			// Add 3 items of a product
			Item item1 = model.addItem("barcode5", entryDate, 0, unit, 0, true);
			Item item2 = model.addItem("barcode5", entryDate, 0, unit, 0, false);
			Item item3 = model.addItem("barcode5", entryDate, 0, unit, 0, false);
			
			// Remove the 1 item
			model.removeItem(item1);
			Calendar exitTime = createCalendar("05/26/2012 05:00 PM");
			item1.setExitTime(exitTime);
			
			// Run the report
			ProductStatisticsGuide guide = new ProductStatisticsGuide(3);
			List<ProductStatisticsNode> nodes = guide.getProductStatistics();
			assertTrue(nodes.size() == 1);
			
			ProductStatisticsNode node = nodes.get(0);
			String test = node.getSupplyCur();
			assertTrue(test.contains("2"));		
			
		} catch (ParseException e) {
			System.out.println(e.getMessage());
			//e.printStackTrace();
			fail();
		} catch (Exception e){
			System.out.println(e.getMessage());
			//e.printStackTrace();
			fail();
		}
		
	}

	@Test
	public void highVolumeProductUseTest()
	{
		try {
			String entry = "10/20/2012 02:00 AM";
			Calendar entryDate = createCalendar(entry);
			// Create a storage Unit
			StorageUnit unit = model.addStorageUnit("testUnit6");
			// add product
			Product p = createProduct("barcode6", "TestProduct", Unit.count,
									  0f, 0, 0, entry);
			model.addProduct(p);
			
			// add 100 items
			ArrayList<Item> list = new ArrayList<Item>();
			
			int i;
			for(i = 0; i<100; i++)
			{
				list.add(model.addItem("barcode6", entryDate, 0, unit, 0, true));
			}
			
			//remove 90
			for(i = 0; i<90; i++)
			{
				Item item = list.get(i);
				model.removeItem(item);
				Calendar exitTime = createCalendar("11/15/2012 05:00 PM");
				item.setExitTime(exitTime);
			}
			
			// Run the report			
			ProductStatisticsGuide guide = new ProductStatisticsGuide(3);
			List<ProductStatisticsNode> nodes = guide.getProductStatistics();
			assertTrue(nodes.size() == 1);
			
			ProductStatisticsNode node = nodes.get(0);
			String test = node.getSupplyCur();
			assertTrue(test.contains("10"));
		}
		catch (ParseException e) {
			System.out.println(e.getMessage());
			//e.printStackTrace();
			fail();
		} catch (Exception e){
			System.out.println(e.getMessage());
			e.printStackTrace();
			fail();
		}
	}

	@Test
	public void mediumVolumeProductUseTest()
	{
		try {
			String entry = "02/20/2012 02:00 AM";
			Calendar entryDate = createCalendar(entry);
			String entry1 = "11/20/2012 02:00 AM";
			Calendar entryDate2 = createCalendar(entry1);
			// Create a storage Unit
			StorageUnit unit = model.addStorageUnit("testUnit7");
			// add product
			Product p = createProduct("barcode7", "TestProduct", Unit.count,
									  0f, 0, 0, entry);
			model.addProduct(p);
			
			// add 15 items
			ArrayList<Item> list = new ArrayList<Item>();
			
			int i;
			for(i = 0; i<15; i++)
			{
				list.add(model.addItem("barcode7", entryDate2, 0, unit, 0, true));
			}
			
			//remove 13
			for(i = 0; i<13; i++)
			{
				Item item = list.get(i);
				model.removeItem(item);
				Calendar exitTime = createCalendar("11/26/2012 05:00 PM");
				item.setExitTime(exitTime);
			}
			
			// Run the report
			ProductStatisticsGuide guide = new ProductStatisticsGuide(3);
			List<ProductStatisticsNode> nodes = guide.getProductStatistics();
			assertTrue(nodes.size() == 1);
			
			ProductStatisticsNode node = nodes.get(0);
			String test = node.getSupplyCur();
			assertTrue(test.contains("2"));
			
			
		}
		catch (ParseException e) {
			System.out.println(e.getMessage());
			//e.printStackTrace();
			fail();
		} catch (Exception e){
			System.out.println(e.getMessage());
			//e.printStackTrace();
			fail();
		}
	}

/*
	Test 8: Test sorting of the data by product description
	a.	No products in the system
	b.	One product in the system
	c.	Many products in the system (e.g. ~15)
*/
	@Test
	public void testSortingofData()
	{
		try {
			String entry = "02/20/2012 02:00 AM";
			Calendar entryDate = createCalendar(entry);
			String entry1 = "11/20/2012 02:00 AM";
			Calendar entryDate2 = createCalendar(entry1);
			// Create a storage Unit
			StorageUnit unit = model.addStorageUnit("testUnit8");
			// add product
			Product p = createProduct("barcode8", "TestProduct", Unit.count,
									  0f, 0, 0, entry);
			
			//TEST WITH NO PRODUCTS
			// Run the report
			ProductStatisticsGuide guide = new ProductStatisticsGuide(3);
			List<ProductStatisticsNode> nodes = guide.getProductStatistics();
			assertTrue(nodes.size() == 0);
			
			//Add one product
			model.addProduct(p);
			
			// add 5 items
			ArrayList<Item> list = new ArrayList<Item>();
			
			int i;
			for(i = 0; i<5; i++)
			{
				list.add(model.addItem("barcode8", entryDate2, 0, unit, 0, true));
			}
			//remove 3
			for(i = 0; i<3; i++)
			{
				Item item = list.get(i);
				model.removeItem(item);
				Calendar exitTime = createCalendar("11/26/2012 05:00 PM");
				item.setExitTime(exitTime);
			}
			
			//TEST WITH ONE PRODUCT
			// Run the report
			guide = new ProductStatisticsGuide(3);
			nodes = guide.getProductStatistics();
			assertTrue(nodes.size() == 1);
			
			//add more products
			Product p2 = createProduct("barcode9", "TestProduct", Unit.count,
					  0f, 0, 0, entry);
			model.addProduct(p2);
			model.addItem("barcode9", entryDate2, 0, unit, 0, true);
			Product p3 = createProduct("barcode10", "TestProduct", Unit.count,
					  0f, 0, 0, entry);
			model.addProduct(p3);
			model.addItem("barcode10", entryDate2, 0, unit, 0, true);
			Product p4 = createProduct("barcode11", "TestProduct", Unit.count,
					  0f, 0, 0, entry);
			model.addProduct(p4);
			model.addItem("barcode11", entryDate2, 0, unit, 0, true);
			Product p5 = createProduct("barcode12", "TestProduct", Unit.count,
					  0f, 0, 0, entry);
			model.addProduct(p5);
			model.addItem("barcode12", entryDate2, 0, unit, 0, true);
			Product p6 = createProduct("barcode13", "TestProduct", Unit.count,
					  0f, 0, 0, entry);
			model.addProduct(p6);
			model.addItem("barcode13", entryDate2, 0, unit, 0, true);
			Product p7 = createProduct("barcode14", "TestProduct", Unit.count,
					  0f, 0, 0, entry);
			model.addProduct(p7);
			model.addItem("barcode14", entryDate2, 0, unit, 0, true);
			Product p8 = createProduct("barcode15", "TestProduct", Unit.count,
					  0f, 0, 0, entry);
			model.addProduct(p8);
			model.addItem("barcode15", entryDate2, 0, unit, 0, true);
			Product p9 = createProduct("barcode16", "TestProduct", Unit.count,
					  0f, 0, 0, entry);
			model.addProduct(p9);
			model.addItem("barcode16", entryDate2, 0, unit, 0, true);
			Product p10 = createProduct("barcode17", "TestProduct", Unit.count,
					  0f, 0, 0, entry);
			model.addProduct(p10);
			model.addItem("barcode17", entryDate2, 0, unit, 0, true);
			Product p11 = createProduct("barcode18", "TestProduct", Unit.count,
					  0f, 0, 0, entry);
			model.addProduct(p11);
			model.addItem("barcode18", entryDate2, 0, unit, 0, true);

			//TEST WITH MANY PRODUCTs
			// Run the report
			guide = new ProductStatisticsGuide(3);
			nodes = guide.getProductStatistics();
			assertTrue(nodes.size() == 11);
		}
		catch (ParseException e) {
			System.out.println(e.getMessage());
			//e.printStackTrace();
			fail();
		} catch (Exception e){
			System.out.println(e.getMessage());
			//e.printStackTrace();
			fail();
		}
	}
	
	private Product createProduct(String productBarcode, String desc, Unit unit,
								  float size, int shelfLife, int three, String date) 
								  throws ParseException
	{
		Calendar newDate = createCalendar(date);
		Barcode bCode = new Barcode(productBarcode);
		Amount amt = new Amount(unit,size);
		int shelf_life = shelfLife;
		int three_month_supply = three;

		Product newProduct = new Product(newDate, bCode, desc, amt,
				 shelf_life, three_month_supply);
		return newProduct;
	}
	
	private Calendar createCalendar(String date) throws ParseException
	{
		String entry = date;
		Date ed = DateUtils.parseDateTime(entry);
		Calendar newDate = new GregorianCalendar();
		newDate.setTime(ed);
		return newDate;
	}
}
